#ifndef COMPARABLE_H
#define COMPARABLE_H

#include <stdint.h>

#define PROJECTION_TYPE int_fast32_t
#define BYTE unsigned char
#define PROJECTION_BODY_SIZE (sizeof(T)/sizeof(PROJECTION_TYPE))
#define PROJECTION_TAIL_SIZE (sizeof(T) - (PROJECTION_BODY_SIZE * sizeof(PROJECTION_TYPE)))

#define cast_ptr(t) ((Projection<PROJECTION_BODY_SIZE, PROJECTION_TAIL_SIZE>*)t)

typedef int_least64_t comparison_t;

template <typename T>
class BitOperator {
	
	template <typename A, unsigned int S>
	struct Bitstring {
		A string[S];
		inline static bool Compare(const A *first, const A *second, comparison_t &result) {
			return (result = (*first - *second)) || Bitstring<A, S - 1>::Compare(++first, ++second, result);
		}

		inline static bool AreIdentical(const A *first, const A *second) {
			return (*first == *second) && Bitstring<A, S - 1>::AreIdentical(++first, ++second);
		}

		inline static void Copy(const A *source, const A *target) {
			*target = *source;
			Bitstring<A, S - 1>(++source, ++target);
		}
	};

	template <typename A>
	struct Bitstring<A, 1> {
		A string[1];
		inline static bool Compare(const A *first, const A *second, comparison_t &result) {
			return (result = (*first - *second));
		}

		inline static bool AreIdentical(const A *first, const A *second) {
			return *first == *second;
		}

		inline static void Copy(const A *source, const A *target) {
			*target = *source;
		}
	};

	template <unsigned int B, unsigned int T>
	struct Projection {
		Bitstring<PROJECTION_TYPE, B> body;
		Bitstring<BYTE, T> tail;
		inline static bool Compare(const Projection *first, const Projection *second, comparison_t &result) {
			return Bitstring<PROJECTION_TYPE, B>::Compare(first->body.string, second->body.string, result) 
				|| Bitstring<BYTE, T>::Compare(first->tail.string, second->tail.string, result);
		}

		inline static bool AreIdentical(const Projection *first, const Projection *second) {
			return Bitstring<PROJECTION_TYPE, B>::AreIdentical(first->body.string, second->body.string)
				&& Bitstring<BYTE, T>::AreIdentical(first->tail.string, second->tail.string);
		}

		inline static void Copy(const Projection *source, const Projection *target) {
			BitString<PROJECTION_TYPE, B>::Copy(source->body.string, target->body.string);
			BitString<BYTE, T>::Copy(source->tail.string, target->tail.string);
		}
	};

	template <unsigned int B>
	struct Projection<B, 0> {
		Bitstring<PROJECTION_TYPE, B> body;
		inline static bool Compare(const Projection *first, const Projection *second, comparison_t &result) {
			return Bitstring<PROJECTION_TYPE, B>::Compare(first->body.string, second->body.string, result);
		}

		inline static bool AreIdentical(const Projection *first, const Projection *second) {
			return Bitstring<PROJECTION_TYPE, B>::AreIdentical(first->body.string, second->body.string);
		}

		inline static void Copy(const Projection *source, const Projection *target) {
			BitString<PROJECTION_TYPE, B>::Copy(source->body.string, target->body.string);
		}
	};

	template <unsigned int T>
	struct Projection<0, T> {
		Bitstring<BYTE, T> tail;
		inline static bool Compare(const Projection *first, const Projection *second, comparison_t &result) {
			return Bitstring<BYTE, T>::Compare(first->tail.string, second->tail.string, result);
		}

		inline static bool AreIdentical(const Projection *first, const Projection *second) {
			return Bitstring<BYTE, T>::AreIdentical(first->tail.string, second->tail.string);
		}

		inline static void Copy(const Projection *source, const Projection *target) {
			BitString<BYTE, T>::Copy(source->tail.string, target->tail.string);
		}
	};
	
public:
	static bool Compare(const T *first, const T *second, comparison_t &result) {
		return Projection<PROJECTION_BODY_SIZE, PROJECTION_TAIL_SIZE>::Compare(cast_ptr(first), cast_ptr(second), result);
	}
	static bool AreIdentical(const T *first, const T *second) {
		return Projection<PROJECTION_BODY_SIZE, PROJECTION_TAIL_SIZE>::AreIdentical(cast_ptr(first), cast_ptr(second));
	}
	static void Copy(const T *source, const T *target) {
		return Projection<PROJECTION_BODY_SIZE, PROJECTION_TAIL_SIZE>::Copy(cast_ptr(source), cast_ptr(target));
	}
};
#endif